package qq2564874169.tuaotuao.fx;

import java.util.Collection;
import java.util.Random;
import java.util.concurrent.*;

public class Task {
    private static ThreadPoolExecutor pool;
    private static boolean isExit;
    private Action1T<Object> func;
    private Object state;
    private int status;
    private Action1T<Task> completed;
    private Exception error;
    private boolean waiting;
    private long id;

    static {
        Runtime.getRuntime().addShutdownHook(new Thread(() -> isExit = true));
        pool = new ThreadPoolExecutor(10, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<>(), new ThreadFactory() {
            private long index;

            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "task-thread-" + (++index));
            }
        });
    }

    public static void setMinSize(int size) {
        pool.setCorePoolSize(size);
    }

    public static void setMaxSize(int size) {
        pool.setMaximumPoolSize(size);
    }

    public Object getState() {
        return state;
    }

    public Throwable getError() {
        return error;
    }

    public long getId() {
        return id;
    }

    public Task(Action action) {
        this.func = p -> action.execute();
    }

    public Task(Action1T<Object> action) {
        this.func = action;
    }

    public Task onCompleted(Action1T<Task> completed) {
        this.completed = completed;
        return this;
    }

    private void onExecute() {
        try {
            id = Task.id();
            func.execute(state);
        } catch (Exception e) {
            error = e;
        } finally {
            if (completed != null) {
                completed.execute(this);
            }
            if (waiting) {
                synchronized (this) {
                    this.notify();
                }
            }
            status = 2;
        }
    }

    public void start(Object state) {
        FxAssert.isNotEquals(status, 1, "当前任务正在执行。");
        this.state = state;
        this.status = 1;
        Task.pool.execute(this::onExecute);
    }

    public void start() {
        start(null);
    }

    public void await() {
        FxAssert.isNotEquals(status, 0, "任务未启动");
        if (status != 2) {
            waiting = true;
            synchronized (this) {
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    //...
                }
            }
        }
    }

    public static Task run(Action execute) {
        Task t = new Task(execute);
        t.start();
        return t;
    }

    public static Task run(Action1T<Object> execute, Object state) {
        Task t = new Task(execute);
        t.start(state);
        return t;
    }

    public static void delay(int millis) {
        delay((long) millis);
    }

    public static void delay(long millis) {
        long exp = System.currentTimeMillis() + millis;
        do {
            try {
                Thread.sleep(exp - System.currentTimeMillis());
            } catch (InterruptedException e) {
                //...
            }
        } while (exp > System.currentTimeMillis() && isExit == false);
    }

    public static void delay(long min, long max) {
        if (max <= min) {
            return;
        }
        long r = max - min;
        long s = Math.abs(new Random().nextLong());
        long t;
        if (s < r) {
            t = s;
        } else {
            t = s % r;
        }
        Task.delay(min + t);
    }

    public static <T> void foreach(Collection<T> collection, Action1T<T> action, int min) {
        if (collection.isEmpty()) {
            return;
        }
        Object[] temp = collection.toArray();
        T first = (T) temp[0];
        long start = System.nanoTime();
        action.execute(first);
        if (temp.length == 1) {
            return;
        }
        Object[] items = new Object[temp.length - 1];
        long val = (System.nanoTime() - start) * items.length;
        System.arraycopy(temp, 1, items, 0, items.length);
        int maxThread = Runtime.getRuntime().availableProcessors() * 4;
        int thCount = (int) (val / 1000000000);
        if (thCount > maxThread) {
            thCount = maxThread;
        }
        if (items.length >= min && thCount < min) {
            thCount = min;
        }
        if (val < 1000 || thCount <= 1) {
            for (Object item : items) {
                action.execute((T) item);
            }
        } else {
            Task.run(state -> {
                int index = 0;
                int tCount = (int) state;
                int iCount = Math.max((items.length) / tCount, 1);
                ConcurrentLinkedQueue<Throwable> exQueue = new ConcurrentLinkedQueue<>();
                CountDownLatch latch = new CountDownLatch(tCount);
                for (int i = 0; i < tCount; i++) {
                    int count = iCount;
                    if (i == tCount - 1 && iCount * tCount < items.length) {
                        count = items.length - (iCount * i);
                    }
                    Object[] execItems = new Object[count];
                    System.arraycopy(items, index, execItems, 0, count);
                    index += count;
                    Task.run(itemsState -> {
                        for (T item : (T[]) itemsState) {
                            action.execute(item);
                        }
                    }, execItems).onCompleted(t -> {
                        if (t.getError() != null) {
                            exQueue.offer(t.getError());
                        }
                        latch.countDown();
                    });
                }
                try {
                    latch.await();
                } catch (InterruptedException e) {
                    //...
                }
                if (!exQueue.isEmpty()) {
                    RuntimeException ex = new RuntimeException("发生了多个异常");
                    for (Throwable throwable : exQueue) {
                        if (throwable != null) {
                            ex.addSuppressed(throwable);
                        }
                    }
                    throw ex;
                }
            }, thCount).onCompleted(t -> {
                if (t.getError() != null) {
                    throw new RuntimeException("任务执行发生异常", t.getError());
                }
            }).await();
        }
    }

    public static <T> void foreach(Collection<T> collection, Action1T<T> action) {
        foreach(collection, action, 1);
    }

    public static long id() {
        return Thread.currentThread().getId();
    }
}
